NSI Terminale
Les premiers termes : 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
Que se passe-t-il pour calculer fib(5) ?
fib(5)fib(n), le nombre d'appels est de l'ordre de 2n
fib(40) → plus de 300 millions d'appels !
Complexité : O(2n) — exponentielle, donc inutilisable pour de grandes valeurs
Cette approche est dite descendante (top-down) : on part du problème général et on descend vers les sous-problèmes.
def fib_memo(n, cache={}):
if n in cache:
return cache[n] # déjà calculé !
if n <= 1:
return n
cache[n] = fib_memo(n - 1, cache) + fib_memo(n - 2, cache)
return cache[n]
F(k) n'est calculée qu'une seule fois.fib_memo(n) : n appels seulement.
| Méthode | fib(30) | fib(40) | Complexité |
|---|---|---|---|
| Naïve | ~1 s | ~100 s | O(2n) |
| Mémoïsation | instantané | instantané | O(n) |
Cette approche est dite ascendante (bottom-up).
def fib_dp(n):
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
dpPour fib_dp(8) :
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|---|
| dp[i] | 0 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 |
O(1) en mémoire.
| Approche | Récursion | Temps | Espace |
|---|---|---|---|
| Naïve | ✔ | O(2n) | O(n) |
| Mémoïsation | ✔ | O(n) | O(n) |
| Bottom-up (tableau) | ✘ | O(n) | O(n) |
| Bottom-up optimisé | ✘ | O(n) | O(1) |
Comment rendre la monnaie avec le minimum de pièces ?
Il faut une approche plus intelligente !
dp[i] = nombre minimum de pièces pour rendre le montant idp[0] = 0c disponible telle que c ≤ i :dp[i] = min(dp[i], dp[i - c] + 1)
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| dp[i] | 0 | 1 | 2 | 1 | 1 | 2 | 2 |
def rendu_monnaie(montant, pieces):
dp = [float('inf')] * (montant + 1)
dp[0] = 0
for i in range(1, montant + 1):
for c in pieces:
if c <= i and dp[i - c] + 1 < dp[i]:
dp[i] = dp[i - c] + 1
return dp[montant]
# Exemple
print(rendu_monnaie(48, [30, 24, 12, 6, 3, 1])) # → 2